package gptma.bo.dao.request;

import gptma.bo.aop.Logged;
import gptma.bo.aop.NoResultFailProof;
import gptma.bo.dao.GenericDaoJpaImpl;
import gptma.bo.model.project.Project;
import gptma.bo.model.request.ClientRequest;
import gptma.bo.model.request.RequestEvaluation;
import gptma.bo.model.request.RequestHistoryElement;
import gptma.bo.model.request.RequestType;
import gptma.bo.model.task.Task;
import gptma.bo.search.ClientRequestSearchCriteria;
import gptma.bo.util.GptmaUtils;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;

import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

@Logged
@NoResultFailProof
public class ClientRequestDaoImpl extends
		GenericDaoJpaImpl<ClientRequest, Long> implements ClientRequestDao {

	@Inject
	private Logger log;

	@Override
	@SuppressWarnings("unchecked")
	public List<String> completeRequestReference(String partialReference) {
		String wildcard = new StringBuilder("%").append(partialReference)
				.append("%").toString();
		List<Object[]> list = em
				.createNamedQuery("ClientRequest.completeReference")
				.setParameter("ref", wildcard).getResultList();

		List<String> ret = new ArrayList<String>();
		StringBuilder buff;
		for (Object[] tab : list) {
			buff = new StringBuilder((String) tab[0]).append(" - ");
			String title = (String) tab[1];
			if (title != null && title.length() > 27) {
				title = title.substring(0, 27);
				buff.append(title).append("...");
			} else {
				buff.append(title);
			}

			ret.add(buff.toString());
		}

		return ret;
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<ClientRequest> findByCriteria(
			ClientRequestSearchCriteria criteria) {
		StringBuilder query = new StringBuilder(
				"select r from ClientRequest r where 1=1 ");

		// requestType
		RequestType type = criteria.getRequestType();
		boolean typeSet = false;
		if (type != null) {
			query.append("and r.requestType = :type ");
			typeSet = true;
		}

		// title
		String title = criteria.getTitle();
		boolean titleSet = false;
		if (!StringUtils.isEmpty(title)) {
			query.append("and r.title like :title ");
			titleSet = true;
		}

		// dates
		Date dateFrom = criteria.getCreationDateFrom();
		Date dateTo = criteria.getCreationDateTo();
		boolean fromSet = (dateFrom != null);
		boolean toSet = (dateTo != null);
		boolean rangeSet = (fromSet && toSet);

		if (rangeSet) {
			query.append("and r.creationDate between :dateFrom and :dateTo ");
		} else if (fromSet) {
			query.append("and r.creationDate >= :dateFrom ");
		} else if (toSet) {
			query.append("and r.creationDate <= :dateTo ");
		}

		// projects
		List<Project> projects = criteria.getProjects();
		boolean projectsSet = false;
		if (projects != null && !projects.isEmpty()) {
			query.append("and r.project in (:projects)");
			projectsSet = true;
		}

		String qStr = query.toString();
		log.debug("Generated query: {}", qStr);

		Query q = em.createQuery(qStr);
		if (typeSet) {
			q.setParameter("type", type);
		}
		if (titleSet) {
			q.setParameter("title", generateContainsCriteria(title));
		}
		if (rangeSet) {
			q.setParameter("dateFrom", dateFrom);
			q.setParameter("dateTo", dateTo);
		} else if (fromSet) {
			q.setParameter("dateFrom", dateFrom);
		} else if (toSet) {
			q.setParameter("dateTo", dateTo);
		}
		if (projectsSet) {
			q.setParameter("projects", projects);
		}

		return q.getResultList();
	}

	@Override
	public ClientRequest findByReference(String reference) {
		return (ClientRequest) em.createNamedQuery("ClientRequest.byReference")
				.setParameter("ref", reference).getSingleResult();
	}

	@Override
	public RequestEvaluation findEvaluation(ClientRequest clientRequest) {
		return (RequestEvaluation) em
				.createNamedQuery("ClientRequest.findEvaluation")
				.setParameter("request", clientRequest).getSingleResult();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<RequestHistoryElement> findClientHistory(
			ClientRequest clientRequest) {
		return em.createNamedQuery("ClientRequest.findClientHistory")
				.setParameter("request", clientRequest).getResultList();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<ClientRequest> findNewRequests(int number) {
		return em.createNamedQuery("ClientRequest.byModificationDate")
				.setParameter("date", new Date(0))
				.setHint(HIBERNATE_QUERY_CACHE, true).setMaxResults(number)
				.getResultList();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<ClientRequest> findNewRequestsFiltered(int dayNumber) {
		// request that have been modified since dayNumber days
		Calendar cal = Calendar.getInstance(Locale.FRANCE);
		cal.add(Calendar.DATE, -dayNumber);

		// reset time info on parameter so that query can be cached
		GptmaUtils.resetTimeInfo(cal);

		return em.createNamedQuery("ClientRequest.byModificationDate")
				.setParameter("date", cal.getTime())
				.setHint(HIBERNATE_QUERY_CACHE, true).getResultList();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<ClientRequest> findNonAttached() {
		return em.createNamedQuery("ClientRequest.findNonAttached")
				.getResultList();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Task> findTasks(ClientRequest clientRequest) {
		return em.createNamedQuery("ClientRequest.findTasks")
				.setParameter("request", clientRequest).getResultList();
	}

}
